import axios, { AxiosInstance } from 'axios'
import qs from 'qs'
import { WechatConfigType, WECHAT_STATE_TYPE} from '../type/index'
import { BaseAuth } from './BaseAuth'


const axisoInstance: AxiosInstance = axios.create()
axisoInstance.defaults.headers.post['Content-type'] = 'application/x-www-form-urlencode'

/**
 * 微信授权类
 * @beta
 */
export class WeChatAuth extends BaseAuth {
  static version: string = '__VERSION__'
  /** 私有配置 */
  private _config: WechatConfigType = {
    apiHost: '',
    appid: '',
    componentAppid: '',
    scope: WECHAT_STATE_TYPE.snsapi_base,
    state: 'STATE',
    cookieKey: '',
    cookieTime: 1
  }
  /** 响应类型 */
  private _responseType: string = 'code'
  /** 微信跳转锚点 */
  private _anchor = '#wechat_redirect'
  /**
   * 构造函数
   * @constructor Auth - 构造函数
   * @param config { ConfigType } - 配置参数
   */
  constructor (config: WechatConfigType) {
    super()
    if (!config.appid) {
      console.warn(new Error('微信公众号appid 不能为空'))
    }
    if (!config.componentAppid) {
      console.warn(new Error('第三方平台 componentAppid 不能为空'))
    }
    this.mergetConfig(config)
  }
  /**
   * 获取微信授权地址
   * @returns { string } - 返回地址
   */
  get baseURL (): string {
    if (this._config.scope === WECHAT_STATE_TYPE.snsapi_login) {
      return 'https://open.weixin.qq.com/connect/qrconnect'
    }
    return 'https://open.weixin.qq.com/connect/oauth2/authorize'
  }
  /**
   * 合并配置
   * @param _config { ConfigType } 配置参数
   */
  mergetConfig (config: WechatConfigType) {
    for (const name in config) {
      this._config[name] = config[name] ?? this._config[name]
    }
    if (!this._config.cookieKey) {
      this._config.cookieKey = `${this._config.appid}_${this._config.scope}`
    }
  }
  

  /**
   * 调用授权
   * @returns 
   */
  auth (): Promise<any> {
    if (!this._config.appid) {
      return Promise.reject(new Error('微信公众号appid 不能为空'))
    }
    if (!this._config.componentAppid) {
      return Promise.reject(new Error('第三方平台 componentAppid 不能为空'))
    }
    if (this.accessToken) {
      return Promise.resolve(this.accessToken)
    }

    const { code, appid, state } = WeChatAuth.parseQuery()
    if (code && appid === this._config.appid && state === this.randState) {
      return this.handleToken('/auth/gettoken', {
        code,
        appid: this._config.appid,
        component_appid: this._config.componentAppid
      })
    }
    this.redirectToAuth()
    return Promise.reject(new Error('跳转授权'))
  }

  /**
   * 获取accessToken
   * @returns accessToken
   */
  get accessToken(): string | undefined {
    return WeChatAuth.getCookie(this._config.cookieKey + '_access_token')
  }

  /**
   * 跳转微信授权地址
   * @param url {string} 当前地址
   */
  redirectToAuth (url: string = window.location.href): void {
    // 获取基本的url 适用于VUE HASH模式
    const currentUrl: string = (url.split('?')[0]).split('#')[0]
    // 获取配置文件
    const { appid, scope, componentAppid } = this._config
    // 获取 查询字符串
    let search = WeChatAuth.parseQuery() // 解析query
    if (search.code || search.state || search.appid) {
      delete search.code // 删除 code参数
      delete search.state // 删除 state参数
      delete search.appid // 删除 appid参数
    }
    const qSearch = qs.stringify(search, { encode: false }) // 重新字符串化
    // 生成随机state
    const rand = WeChatAuth.randomStr()
    WeChatAuth.setCookie(this._config.cookieKey + '_rand_state', rand, 0.1) // 6分钟有效期？
    const redirectUri = currentUrl + (qSearch ? `?${qSearch}` : '') + this.formateHash()
    const params = {
      appid,
      redirect_uri: redirectUri,
      response_type: this._responseType,
      scope,
      state: rand,
      component_appid: componentAppid
    }
    const href = `${this.baseURL}?${qs.stringify(params)}${this._anchor}`
    location.replace(href)
  }

  /**
   * 格式化hash
   * @returns {string} 返回格式化的hash
   */
  formateHash (): string {
    if (!window.location.hash) return ''
    const hashArr: string[] = window.location.hash.split('?')
    const hash: string = hashArr[0].replace(/(#\/)*/, '$1')
    const hashSearch: {
      code?: string,
      state?: string,
      [key: string]: any,
    } = hashArr[1] ? {} : {}
    if (hashSearch.code || hashSearch.state) {
      delete hashSearch.code
      delete hashSearch.state
    }
    return hash + ''
  }

  /**
   * 获取rand state
   * @returns {string | undefined} - 返回随机的state
   */
  get randState (): string | undefined {
    return WeChatAuth.getCookie(this._config.cookieKey + '_rand_state')
  }

  /**
   * 更新 *refresh_token*
   * @return {Promise<unknown>} - 刷新
   */
   updateToken (): Promise<unknown> | void {
    // 如果刷新TOKEN 过期 , 那么就重新去授权
    if (!this.refreshToken) {
      this.redirectToAuth()
      return
    }
    return this.handleToken('/auth/refreshtoken', {
      appid: this._config.appid,
      refresh_token: this.refreshToken
    })
  }

  /**
   * 获取 *refresh_token*
   * @returns { string | undefined } - 返回刷新token
   */
  get refreshToken (): string | undefined {
    return WeChatAuth.getCookie(this._config.cookieKey + '_refresh_token')
  }

  /**
   * 获取用户的信息
   * @returns {string | null | undefined} - 用户信息
   */
  get userInfo(): string | null | undefined {
    if (this._config.scope === WECHAT_STATE_TYPE.snsapi_base) {
      return ''
    }
    return WeChatAuth.getCookie(this._config.cookieKey + '_userInfo')
  }

  /**
   * 获取code
   * @returns { any } - 返回 code
   */
  get code (): any {
    return WeChatAuth.getCookie(this._config.cookieKey + '_code')
  }
  /**
   * 
   * @param path { string } - 请求地址
   * @param params { any } - 请求参数
   * @returns {Promise<any>} - 返回 Promise 对象
   */
  private ajax (path: string, params: any): Promise<any> {
    return axisoInstance.post(this._config.apiHost + path, qs.stringify(params))
  }

  /**
   * 请求token
   * @param path
   * @param params
   * @return {Promise<unknown>}
   */
   handleToken (path: string, params: any): Promise<unknown>  {
    return new Promise((resolve, reject) => {
      this.ajax(path, params).then(({ data }) => {
        // eslint-disable-next-line camelcase
        const { status, message, data: { access_token, refresh_token, userInfo } } = data
        // eslint-disable-next-line camelcase
        if (access_token && refresh_token && status === 1) {
          WeChatAuth.removeAllCookie()
          WeChatAuth.setCookie(this._config.cookieKey + '_access_token', access_token, this._config.cookieTime) // 更新 access_token 0.5小时
          WeChatAuth.setCookie(this._config.cookieKey + '_refresh_token', refresh_token, this._config.cookieTime! + 1) // 更新 refresh_token 1小时
          userInfo && WeChatAuth.setCookie(this._config.cookieKey + '_userInfo', JSON.stringify(userInfo), this._config.cookieTime) // 更新用户信息 0.5小时
          resolve(access_token)
        } else {
          reject(message)
          this.redirectToAuth() // 去重新授权
        }
      })
    })
  }
}